package com.brownalexis.application.controller;

/**
 * @Author: brownalexis
 * @Date: 2024/04/18 16:15
 * @Description:
 */

import com.brownalexis.application.mapper.FileChunkMapper;
import com.brownalexis.application.mapper.FileStorageMapper;
import com.brownalexis.application.model.FileChunk;
import com.brownalexis.application.model.FileStorage;
import com.brownalexis.application.params.FileParams;
import com.brownalexis.application.query.FileChunkQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.OutputStream;
import java.nio.file.*;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

@Service
public class FileStorageService {

    @Value("${file.storage.location}")
    private String storageLocation;

    @Resource
    private FileChunkMapper fileChunkMapper;

    @Resource
    private FileStorageMapper fileStorageMapper;

    @PostConstruct
    public void init() {
        Path storagePath = Paths.get(storageLocation);
        if (!Files.exists(storagePath)) {
            try {
                Files.createDirectories(storagePath);
            } catch (IOException e) {
                throw new RuntimeException("Could not initialize storage", e);
            }
        }
    }

    public void saveChunk(MultipartFile file,
                          FileParams params) {
        byte[] data = null;
        String fileName ="";
        int chunkIndex =0;
        String identifier="";
        int totalChunk=0;
        Long totalSize=0L;
        String newFileName ="";
        try {
            data = file.getBytes();
             fileName = params.getFileName();
             chunkIndex = params.getChunkIndex();
             identifier = params.getIdentifier();
             totalChunk = params.getChunks();
             totalSize = params.getTotalSize();
            if (StringUtils.isBlank(identifier)) {
                throw new RuntimeException("唯一标识符不能未空");
            }
            newFileName = fileName + "_chunk_" + chunkIndex;
            Path fileChunkPath = Paths.get(storageLocation, identifier, newFileName);
            // 确保目录存在
            Files.createDirectories(fileChunkPath.getParent());
            Files.write(fileChunkPath, data);
        } catch (IOException ioException) {
            ioException.printStackTrace();
            throw  new RuntimeException("上传失败");
        }
        try {
            //增加数据库信息
            FileChunk fileChunk = new FileChunk();
            fileChunk.setFileName(newFileName);
            fileChunk.setIdentifier(identifier);
            fileChunk.setCreateTime(LocalDateTime.now());
            fileChunk.setChunkNumber(chunkIndex);
            fileChunk.setTotalChunk(totalChunk);
            fileChunk.setTotalSize(totalSize);
            fileChunk.setCreateBy(params.getUserId());
            fileChunk.setCurrentChunkSize(params.getCurrentChunkSize());
            fileChunk.setChunkSize(params.getChunkSize());
            fileChunkMapper.insert(fileChunk);
        }catch (Exception sqlException){
            sqlException.printStackTrace();
            throw  new RuntimeException("新增信息失败，请联系工作人员");
        }

    }

    public void mergeFile(String fileName, int chunks, String identifier) throws IOException {
        //插入数据库数据
        FileStorage fileStorage=new FileStorage();
        fileStorage.setRealFileName(fileName);
        fileStorage.setIdentifier(identifier);
        fileStorage.setCreateBy("123");
        fileStorage.setCreateTime(LocalDateTime.now());
        fileStorage.setStatus("error");
        fileStorageMapper.insert(fileStorage);

        Path tempDirPath = Paths.get(storageLocation, identifier);
        // 最终文件存储位置
        Path finalPath = Paths.get(storageLocation);
        Path mergedFilePath = tempDirPath.resolve(fileName);

        try (OutputStream out = Files.newOutputStream(mergedFilePath, StandardOpenOption.CREATE)) {
            for (int i = 0; i < chunks; i++) {
                Path chunkPath = tempDirPath.resolve(fileName + "_chunk_" + i);
                Files.copy(chunkPath, out);
                // 删除已合并的分片
                Files.delete(chunkPath);
            }
        }

        // 移动合并后的文件到指定的最终位置
        Files.move(mergedFilePath, finalPath.resolve(fileName), StandardCopyOption.REPLACE_EXISTING);

        // 如果目录现在为空，可以删除该目录
        try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(tempDirPath)) {
            if (!dirStream.iterator().hasNext()) {
                Files.delete(tempDirPath);
            }
        }
        fileStorage.setStatus("success");
        fileStorageMapper.update(fileStorage);
    }

    public void deleteFileChunks(String fileName, String identifier,Long userId) throws IOException {
        Path dirPath = Paths.get(storageLocation, identifier);
        Files.walk(dirPath)
                .filter(path -> path.toString().contains(fileName))
                .forEach(path -> {
                    try {
                        Files.deleteIfExists(path);
                    } catch (IOException e) {
                        throw new RuntimeException("Failed to delete file chunk: " + path, e);
                    }
                });
        // Optionally, clean up the directory if it's empty
        if (!Files.list(dirPath).findAny().isPresent()) {
            Files.deleteIfExists(dirPath);
        }

        fileChunkMapper.deleteFile(userId,fileName,identifier);
    }



    public List<Integer> getFileChunk(FileChunkQuery query){
        List<Integer> list=new ArrayList<>(200);
        Stream<FileChunk> fileChunkStream = fileChunkMapper.getFileChunk(query).stream();
        fileChunkStream.forEach(fileChunk -> {
            list.add(fileChunk.getChunkNumber());
        });
        return  list;
    }

}

